# -*- coding: utf-8 -*-
from django import forms
from django.http import HttpResponseRedirect

from fields import Field, ChoiceField, LinkField, SearchField
from callbacks import *

class FilterForm(forms.Form):
    """
    This form can be used for building filters (usually using select boxes or 
    simple links). This way the user can easily arrange the results displayed 
    in the index page.
    
    Example usage
    ~~~~~~~~~~~~~
    
    **THE VIEW**::
    
        from yafinder import filters
    
        objects = Article.objects.all()
        
        # filters
        filter_form = filters.FilterForm((
        
            filters.LinkField(label="Name starts with", allow_none="All", 
                callback=filters.lookup_for_letter("name")),
                
            filters.Field(label="Category", allow_none="All",
                choices=Category.objects.values_list("slug", "name"),
                callback=filters.lookup_for("category__slug")),
                
            ), data=request.GET)
        
        if filter_form.is_valid():
            objects = filter_form.run(objects)
        else:
            return filter_form.redirect()
        
        # context
        context = {
            "objects": objects,
            "filter_form": filter_form,
        }
        return render_to_response(template, context, 
            context_instance=RequestContext(request))
            
    Note that fields is passed to *FilterForm* as a sequence (not as a dict).
    A field name (the key in fields dict) is normally generated slugifying 
    the passed label, but can be manually overridden passing 
    *name="my-name"* to the field constructor.
            
    **THE TEMPLATE**::
        
        # you can customize page title
        
        Article Index {{ filter_form.title }} - 
        
        # you can show filters with standard *as_p* or *as_table* form methods
        
        <form action="" method="get" accept-charset="utf-8">
            {{ filter_form.as_p }}
            <p><input type="submit" value="Filter"></p>
        </form>
        
        # you can show selected choices and the links to remove filters
        
        {% for field in filter_form.selected_fields %}
            {{ field.label }}: {{ field.value_display }} 
            (<a href="{{ field.url_without_me }}">remove</a>)<br />
        {% endfor %}
        
    The **title** of single fields can be customized subclassing *FilterForm*
    and overriding the *get_field_title(field)* method, or just defining
    *YAFINDER_TITLE_PATTERN* in your settings file 
    (defaults to " - %(label)s: %(value_display)s").
    
    You can attach **custom callbacks** to fields, remembering that:
        - the callback takes 2 arguments: the field itself and the queryset to
          manipulate 
          (and *field.value* is the current selected value for the field)
        - the callback must return the manipulated queryset
        
    The distribution comes with **predefined callback factories**:
        - *yafinder.filters.lookup_for* that takes a django style lookup key
          and returns a callback that filters queryset using that lookup
        - *yafinder.filters.lookup_for_letter* that takes a field name and
          returns a callback useful for by initial letter filtering
        
    You can add **autosubmit** on selection change capabilities in 3 steps:
    
        1. copy to your media directory the Javascript file **autosubmit.js**
           included in ``media/js/`` within the distribution
           
        2. give the class *yaform* to your filter form, e.g.::
        
            <form class="yaform" action="" method="get" accept-charset="utf-8">
                {{ filter_form.as_p }}
                <p><input type="submit" value="Filter"></p>
            </form>
            
        3. load the javascript in the page, e.g.::
            
            {% block js %}
                {{ block.super }}
                <script src="/site_media/js/autosubmit.js" type="text/javascript" charset="utf-8"></script>
            {% endblock %}
    """
    def __init__(self, fields, *args, **kwargs):
        """
        The fields here are passed as a sequence.
        Takes all the other *args and **kwargs accepted by 
        *django.forms.Form* class.
        """
        super(FilterForm, self).__init__(*args, **kwargs)
        for field in fields:
            field.set_querydict(self.data)
            self.fields[field.name] = field
        self._redirect_url = ""
        
    def is_valid(self):
        """
        Simple wrapper around django *form.is_valid* that build the redirect
        url and skip unselected fields non-errors coming from *field.clean()*.
        """
        result = super(FilterForm, self).is_valid()
        if not result:
            querydict = self.data.copy()
            errors = self.errors.copy()
            for key in errors:
                if key in querydict:
                    # the error is in the value of a selected field
                    del querydict[key]
                else:
                    # the error is a non-error (the field is just blank)
                    del self.errors[key]
            has_errors = bool(self.errors)
            if has_errors:
                self._redirect_url = u"?%s" % querydict.urlencode()
            return not has_errors
        return True
                    
    @property
    def selected_fields(self):
        """
        Return a list of selected fields.
        """
        return [i for i in self.fields.values() if i.is_selected]
        
    @property
    def unselected_fields(self):
        """
        Return a list of unselected fields.
        """
        return [i for i in self.fields.values() if not i.is_selected]
        
    def redirect(self):
        """
        Return a redirect response to the cleaned url.
        """
        return HttpResponseRedirect(self._redirect_url)
        
    def run(self, queryset):
        """
        Filter the passed *queryset*.
        The real filtering is done by the callbacks associated to selected
        fields.
        """
        for field in self.selected_fields:
            queryset = field.callback(queryset)
        return queryset
        
    def get_field_title(self, field):
        """
        Override this if you want to customize the field related bit of
        the automatically generated page title.
        """
        return field.title
    
    @property
    def title(self):
        """
        Return an automatically generated page title based on currently
        selected fields.
        """
        return "".join(self.get_field_title(i) for i in self.selected_fields)
